#include "qrender/axis_renderer.h"

#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QThread>

#include <QtMath>

#include <qrender/camera.h>

#include <qrender/sphere_renderer.h>
#include <qrender/cone_renderer.h>
#include <qrender/line_renderer.h>

AxisRenderer::AxisRenderer() : Renderer()
{
    r_origin_.reset(new SphereRenderer());

    r_cone_x_.reset(new ConeRenderer());
    r_cone_y_.reset(new ConeRenderer());
    r_cone_z_.reset(new ConeRenderer());

    r_line_x_.reset(new LineRenderer());
    r_line_y_.reset(new LineRenderer());
    r_line_z_.reset(new LineRenderer());

    auto pos = QVector3D(pose_[0], pose_[1], pose_[2]);

    r_origin_->setPosition(pos);

    r_cone_x_->setPosition(pos);
    r_cone_y_->setPosition(pos);
    r_cone_z_->setPosition(pos);

    r_line_x_->setPosition(pos);
    r_line_y_->setPosition(pos);
    r_line_z_->setPosition(pos);
}

AxisRenderer::~AxisRenderer()
{
    AxisRenderer::release();
}

void AxisRenderer::setPose(const std::vector<double> &pose)
{
    if (pose_ == pose) {
        return;
    }

    pose_ = pose;

    auto pos = QVector3D(pose_[0], pose_[1], pose_[2]);

    r_origin_->setPosition(pos);

    r_cone_x_->setPosition(pos);
    r_cone_y_->setPosition(pos);
    r_cone_z_->setPosition(pos);

    r_line_x_->setPosition(pos);
    r_line_y_->setPosition(pos);
    r_line_z_->setPosition(pos);

    generateAxisVertices();
}

void AxisRenderer::setColor(QVector4D x_color, QVector4D y_color,
                            QVector4D z_color)
{
    if (x_color_ == x_color && y_color_ == y_color && z_color_ == z_color) {
        return;
    }

    x_color_ = x_color;
    y_color_ = y_color;
    z_color_ = z_color;

    r_cone_x_->setColor(x_color);
    r_cone_y_->setColor(y_color);
    r_cone_z_->setColor(z_color);

    r_line_x_->setColor(x_color);
    r_line_y_->setColor(y_color);
    r_line_z_->setColor(z_color);
}

void AxisRenderer::setShape(float cone_height, float cone_radius)
{
    if (cone_height_ == cone_height && cone_radius_ == cone_radius) {
        return;
    }

    cone_height_ = cone_height;
    cone_radius_ = cone_radius;

    generateAxisVertices();
}

void AxisRenderer::setFixedSize(bool fixed_size, float fixed_length)
{
    // 是否固定
    fixed_size_ = fixed_size;
    // 固定的到原点距离
    fixed_distance_ = fixed_length;

    // 取消固定，还原尺寸
    if (!fixed_size_) {
        setShape(fixed_cone_height_, fixed_cone_radius_);
    }
    // 固定，记录固定的尺寸
    else {
        fixed_cone_height_ = cone_height_;
        fixed_cone_radius_ = cone_radius_;
    }
}

void AxisRenderer::create()
{
    r_origin_->requestCreate();

    r_cone_x_->requestCreate();
    r_cone_y_->requestCreate();
    r_cone_z_->requestCreate();

    r_line_x_->requestCreate();
    r_line_y_->requestCreate();
    r_line_z_->requestCreate();

    // 生成顶点数据
    generateAxisVertices();
}

void AxisRenderer::render(const CameraInfo &camera)
{
    auto camera_fixed = camera;
    if (fixed_size_) {
        // 固定显示大小：根据相机到达原点距离的比例，缩放物体尺寸
        auto camera_distance = camera_fixed.camera_eye.distanceToPoint(
            QVector3D(pose_[0], pose_[1], pose_[2]));
        if (last_camera_distance_ != camera_distance) {
            last_camera_distance_ = camera_distance;

            auto scale = camera_distance / fixed_distance_;
            //            qDebug() << __FUNCTION__ << scale << camera_distance
            //                     << fixed_distance_;
            setShape(fixed_cone_height_ * scale, fixed_cone_radius_ * scale);
        }
    }

    // 绘制原点
    r_origin_->requestRender(camera_fixed);

    // 绘制三个轴的箭头 圆锥
    r_cone_x_->requestRender(camera_fixed);
    r_cone_y_->requestRender(camera_fixed);
    r_cone_z_->requestRender(camera_fixed);

    // 绘制三个轴 线段
    r_line_x_->requestRender(camera_fixed);
    r_line_y_->requestRender(camera_fixed);
    r_line_z_->requestRender(camera_fixed);
}

void AxisRenderer::release()
{
    r_origin_->requestRelease();

    r_cone_x_->requestRelease();
    r_cone_y_->requestRelease();
    r_cone_z_->requestRelease();

    r_line_x_->requestRelease();
    r_line_y_->requestRelease();
    r_line_z_->requestRelease();
}

void AxisRenderer::generateAxisVertices()
{
    // NOTE: 切割点数越多，形状越接近
    // 后续可根据缩放距离，动态调整渲染精度

    // 圆锥底座平面切割点数
    static const int cone_slices = 20;
    // 球体经纬度切割点数
    static const int origin_sphere_latitudes = 10;
    static const int origin_sphere_longitudes = 10;

    // 原点球体半径 = 箭头圆锥底座半径 * 2
    const float origin_radius = cone_radius_ * 2;
    // 线段长度 = 箭头圆锥高度 * 2
    const float line_height = cone_height_ * 2;

    // 生成坐标轴顶点数据
    // 原点球体半径 -> 线段长度 -> 箭头圆锥高度

    // 通过 rx,ry,rz 获取 x,y,z 轴方向向量
    QMatrix4x4 matrix;
    matrix.rotate(qRadiansToDegrees(pose_[5]), 0.0, 0.0, 1.0);
    matrix.rotate(qRadiansToDegrees(pose_[4]), 0.0, 1.0, 0.0);
    matrix.rotate(qRadiansToDegrees(pose_[3]), 1.0, 0.0, 0.0);
    const QVector3D x_axis = matrix.column(0).toVector3D();
    const QVector3D y_axis = matrix.column(1).toVector3D();
    const QVector3D z_axis = matrix.column(2).toVector3D();

    // 坐标原点
    r_origin_->setShape(origin_radius, origin_sphere_latitudes,
                        origin_sphere_longitudes, { 0.0, 1.0, 1.0, 1.0 });

    // 三个方向圆锥箭头
    r_cone_x_->setShape(x_axis * (origin_radius + line_height),
                        x_axis * (origin_radius + line_height + cone_height_),
                        cone_radius_, cone_slices, x_color_);
    r_cone_y_->setShape(y_axis * (origin_radius + line_height),
                        y_axis * (origin_radius + line_height + cone_height_),
                        cone_radius_, cone_slices, y_color_);
    r_cone_z_->setShape(z_axis * (origin_radius + line_height),
                        z_axis * (origin_radius + line_height + cone_height_),
                        cone_radius_, cone_slices, z_color_);

    // 三个方向线段
    r_line_x_->setShape(x_axis * (origin_radius - 1),
                        x_axis * (origin_radius + line_height + 1), x_color_);
    r_line_y_->setShape(y_axis * (origin_radius - 1),
                        y_axis * (origin_radius + line_height + 1), y_color_);
    r_line_z_->setShape(z_axis * (origin_radius - 1),
                        z_axis * (origin_radius + line_height + 1), z_color_);
}

void AxisRenderer::scale(CameraInfo &camera, float factor)
{
    camera.camera_eye -= factor * camera.forward;

    camera.view_matrix.setRow(
        0, QVector4D(camera.right,
                     -QVector3D::dotProduct(camera.camera_eye, camera.right)));
    camera.view_matrix.setRow(
        1, QVector4D(camera.up,
                     -QVector3D::dotProduct(camera.camera_eye, camera.up)));
    camera.view_matrix.setRow(
        2, QVector4D(camera.forward, -QVector3D::dotProduct(camera.camera_eye,
                                                            camera.forward)));
    camera.view_matrix.setRow(3, QVector4D(0.0, 0.0, 0.0, 1.0));
}
